package product

import (
	"context"
	"strconv"
	"strings"
	"time"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/utils/pointy"
	"mall/service/backend/api/internal/logic/product/util"
	"mall/service/backend/api/internal/logic/utils"
	"mall/service/backend/api/internal/svc"
	"mall/service/backend/api/internal/types"
	"mall/service/backend/common/errorx"
	"mall/service/backend/common/i18n"
	"mall/service/blog/rpc/blogclient"
	"mall/service/product/rpc/productclient"

	"github.com/zeromicro/go-zero/core/logx"
)

type GetProductByIdLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewGetProductByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetProductByIdLogic {
	return &GetProductByIdLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *GetProductByIdLogic) GetProductById(req *types.IDReq) (resp *types.ProductInfoResp, err error) {
	if req.Id == 0 {
		return nil, errorx.NewInvalidArgumentError(i18n.ValidationError)
	}

	data, err := l.svcCtx.ProductRpc.BackendGetProductById(l.ctx, &productclient.ProductIdReq{
		Id:           req.Id,
		WithMeta:     pointy.GetPointer(uint32(1)),
		WithMedia:    pointy.GetPointer(uint32(1)),
		WithCategory: pointy.GetPointer(uint32(1)),
		WithTag:      pointy.GetPointer(uint32(1)),
		WithEntry:    pointy.GetPointer(uint32(1)),
		WithContent:  pointy.GetPointer(uint32(1)),
		WithAttr:     pointy.GetPointer(uint32(1)),
		WithSpec:     pointy.GetPointer(uint32(1)),
		WithSku:      pointy.GetPointer(uint32(1)),
		WithTracing:  pointy.GetPointer(uint32(1)),
	})
	if err != nil {
		return nil, err
	}

	var publicTime *uint32
	var publicDate *string
	if data.Visibility != nil {
		publicTime = data.Visibility
		if *data.Visibility == 2 {
			publicDate = data.CreatedAt
		}
	}

	var meta []*types.MetaInfo
	var photoCount *uint32
	var viewCount *uint32
	var forwardCount *uint32
	var pendingStatus *uint32

	var gemPriceMin *string
	var gemPriceMax *string
	var retailPriceMin *string
	var retailPriceMax *string
	var saleCount *uint32
	var isNew *uint32
	var isHot *uint32
	var sync *uint32
	var syncAt *string
	var stock *uint32
	var sellPoint *string
	var stockUnit *string
	var showStock *uint32
	var relationPerfumeId *uint64
	var relationPerfumeName *string
	var relationPerfumeSlug *string
	var relationBrandId *uint64
	var relationBrandName *string
	var relationBrandSlug *string
	var perfume *types.KBEntryInfo
	var brand *types.KBEntryInfo
	var abbr *string

	if len(data.Meta) > 0 {
		for _, v := range data.Meta {
			if v.Key != nil && v.Value != nil {
				switch *v.Key {
				case "photoCount":
					if i, err := strconv.ParseInt(*v.Value, 10, 32); err == nil {
						photoCount = pointy.GetPointer(uint32(i))
					}
				case "viewCount":
					if i, err := strconv.ParseInt(*v.Value, 10, 32); err == nil {
						viewCount = pointy.GetPointer(uint32(i))
					}
				case "forwardCount":
					if i, err := strconv.ParseInt(*v.Value, 10, 32); err == nil {
						forwardCount = pointy.GetPointer(uint32(i))
					}
				case "pendingStatus":
					if i, err := strconv.ParseInt(*v.Value, 10, 32); err == nil {
						pendingStatus = pointy.GetPointer(uint32(i))
					}
				case "gemPriceMin":
					gemPriceMin = v.Value
				case "gemPriceMax":
					gemPriceMax = v.Value
				case "retailPriceMin":
					retailPriceMin = v.Value
				case "retailPriceMax":
					retailPriceMax = v.Value
				case "saleCount":
					if i, err := strconv.ParseInt(*v.Value, 10, 32); err == nil {
						saleCount = pointy.GetPointer(uint32(i))
					}
				case "isNew":
					if *v.Value == "1" {
						isNew = pointy.GetPointer(uint32(1))
					}
				case "isHot":
					if *v.Value == "1" {
						isHot = pointy.GetPointer(uint32(1))
					}
				case "sync":
					if *v.Value == "1" {
						sync = pointy.GetPointer(uint32(1))
					}
				case "syncFrom":
					syncAt = v.Value
				case "stock":
					if i, err := strconv.ParseInt(*v.Value, 10, 32); err == nil {
						stock = pointy.GetPointer(uint32(i))
					}
				case "sellPoint":
					sellPoint = v.Value
				case "stockUnit":
					stockUnit = v.Value
				case "showStock":
					if *v.Value == "1" {
						showStock = pointy.GetPointer(uint32(1))
					}
				case "abbr":
					abbr = v.Value
				default:
					meta = append(meta, &types.MetaInfo{
						Id:    v.Id,
						Key:   v.Key,
						Value: v.Value,
					})
				}
			}
		}
	}

	if len(data.Relations) > 0 {
		var ids []string
		for _, v := range data.Relations {
			if v.Id != nil && *v.Id > 0 {
				ids = append(ids, strconv.FormatInt(int64(*v.Id), 10))
			}
		}
		if len(ids) > 0 {
			idEntryMap := make(map[uint64]*types.KBEntryInfo)
			entryList, err := l.svcCtx.BlogRpc.BackendGetKBPostList(l.ctx, &blogclient.TmpKBPostListReq{
				Ids:       pointy.GetPointer(strings.Join(ids, ",")),
				WithMedia: pointy.GetPointer(uint32(1)),
			})
			if err != nil {
				return nil, err
			}

			if entryList.Total > 0 {
				for _, entry := range entryList.List {
					var media []*types.MediaInfo
					if len(entry.Media) > 0 {
						media = append(media, &types.MediaInfo{
							Id:  entry.Media[0].Id,
							Url: entry.Media[0].Url,
						})
					}

					if entry.Id != nil {
						idEntryMap[*entry.Id] = &types.KBEntryInfo{
							BaseIDInfo: types.BaseIDInfo{
								Id: entry.Id,
							},
							Title: entry.Title,
							Slug:  entry.Slug,
							Media: media,
						}
					}
				}
			}
			for _, v := range data.Relations {
				if v.Id != nil && *v.Id > 0 && v.Key != nil {
					switch *v.Key {
					case globalkey.ProductRelationNamePerfume:
						if entry, ok := idEntryMap[uint64(*v.Id)]; ok {
							perfume = entry
							relationPerfumeId = v.Id
							relationPerfumeName = v.Name
							relationPerfumeSlug = v.Slug
						}
					case globalkey.ProductRelationNameBrand:
						if entry, ok := idEntryMap[uint64(*v.Id)]; ok {
							brand = entry
							relationBrandId = v.Id
							relationBrandName = v.Name
							relationBrandSlug = v.Slug
						}
					}
				}
			}
		}
	}

	var media []*types.MediaInfo
	if len(data.Media) > 0 {
		for _, v := range data.Media {
			var url string
			if v.Url != nil && len(*v.Url) > 0 {
				url = *v.Url
			}
			if len(url) == 0 && v.Uri != nil && len(*v.Uri) > 0 {
				url = *v.Uri
			}
			filename, mimeType, metaData := utils.ParseMetaData(v.MetaData)
			media = append(media, &types.MediaInfo{
				Uid:       v.Uid,
				Id:        v.Id,
				Thumbnail: v.Thumbnail,
				Order:     v.Order,
				Url:       pointy.GetPointer(url),
				Name:      pointy.GetPointer(filename),
				MimeType:  pointy.GetPointer(mimeType),
				AltText:   v.AltText,
				MetaData:  pointy.GetPointer(metaData),
			})
		}
	}

	var categories []*types.ProductTermInfo
	if len(data.Categories) > 0 {
		for _, v := range data.Categories {
			categories = append(categories, &types.ProductTermInfo{
				BaseIDInfo: types.BaseIDInfo{
					Id: v.Id,
				},
				Name: v.Name,
				Slug: v.Slug,
			})
		}
	}
	var tags []*types.ProductTermInfo
	if len(data.Tags) > 0 {
		for _, v := range data.Tags {
			tags = append(tags, &types.ProductTermInfo{
				BaseIDInfo: types.BaseIDInfo{
					Id: v.Id,
				},
				Name: v.Name,
				Slug: v.Slug,
			})
		}
	}

	nowString := modelx.FormatTime(time.Now(), "")
	var attrs []*types.ProductAttrInfo
	if len(data.Attrs) > 0 {
		for _, attr := range data.Attrs {
			if len(attr.Options) > 0 {
				var options []*types.ProductAttrOptInfo
				for _, opt := range attr.Options {
					var key *string
					if opt.Uid != nil {
						key = pointy.GetPointer("opt_" + strconv.FormatInt(int64(*opt.Uid), 10) + "_" + nowString)
					} else {
						key = pointy.GetPointer("opt_0_" + nowString)
					}
					options = append(options, &types.ProductAttrOptInfo{
						Uid:    opt.Uid,
						Id:     opt.Id,
						Key:    key,
						Name:   opt.Name,
						Slug:   opt.Slug,
						ImgId:  opt.MediaId,
						ImgUrl: opt.MediaUrl,
					})
				}
				var key *string
				if attr.Uid != nil {
					key = pointy.GetPointer("attr_" + strconv.FormatInt(int64(*attr.Uid), 10) + "_" + nowString)
				} else {
					key = pointy.GetPointer("attr_0_" + nowString)
				}
				attrs = append(attrs, &types.ProductAttrInfo{
					Uid:      attr.Uid,
					Id:       attr.Id,
					Key:      key,
					Name:     attr.Name,
					Slug:     attr.Slug,
					HasImage: attr.HasMedia,
					Options:  options,
				})
			}
		}
	}
	var specs []*types.ProductAttrInfo
	if len(data.Specs) > 0 {
		for _, spec := range data.Specs {
			if len(spec.Options) > 0 {
				var options []*types.ProductAttrOptInfo
				for _, opt := range spec.Options {
					var key *string
					if opt.Uid != nil {
						key = pointy.GetPointer("opt_" + strconv.FormatInt(int64(*opt.Uid), 10) + "_" + nowString)
					} else {
						key = pointy.GetPointer("opt_0_" + nowString)
					}
					options = append(options, &types.ProductAttrOptInfo{
						Uid:    opt.Uid,
						Id:     opt.Id,
						Key:    key,
						Name:   opt.Name,
						Slug:   opt.Slug,
						ImgId:  opt.MediaId,
						ImgUrl: opt.MediaUrl,
					})
				}
				var key *string
				if spec.Uid != nil {
					key = pointy.GetPointer("spec_" + strconv.FormatInt(int64(*spec.Uid), 10) + "_" + nowString)
				} else {
					key = pointy.GetPointer("spec_0_" + nowString)
				}
				specs = append(specs, &types.ProductAttrInfo{
					Uid:      spec.Uid,
					Id:       spec.Id,
					Key:      key,
					Name:     spec.Name,
					Slug:     spec.Slug,
					HasImage: spec.HasMedia,
					Options:  options,
				})
			}
		}
	}
	var skus []*types.ProductVariationInfo
	if len(data.Skus) > 0 {
		for _, sku := range data.Skus {
			var optKeys []string
			if len(sku.Specs) > 0 {
				for _, spec := range sku.Specs {
					if len(spec.Options) > 0 {
						for _, opt := range spec.Options {
							if opt.Uid != nil {
								optKeys = append(optKeys, "opt_"+strconv.FormatInt(int64(*opt.Uid), 10)+"_"+nowString)
							} else {
								optKeys = append(optKeys, "opt_0_"+nowString)
							}
						}
					}
				}
			}

			skus = append(skus, &types.ProductVariationInfo{
				Id:           sku.Id,
				Key:          pointy.GetPointer(strings.Join(optKeys, "_-_")),
				RetailPrice:  sku.RetailPrice,
				CounterPrice: sku.CounterPrice,
				GemPrice:     sku.GemPrice,
				Inventory:    sku.Inventory,
				SkuCode:      sku.SkuCode,
				Status:       sku.Status,
				Weight:       sku.Weight,
				SaleCount:    sku.SaleCount,
				ImgId:        sku.MediaId,
				ImgUrl:       sku.MediaUrl,
			})
		}
	}

	option := util.GetAuditOptionByStatus(data.Status)
	var uri *string
	if data.Uri != nil && len(*data.Uri) > 0 {
		uri = pointy.GetPointer(l.svcCtx.Config.NfsConf.SiteUrl + *data.Uri)
	}

	return &types.ProductInfoResp{
		BaseDataInfo: types.BaseDataInfo{
			Success: true,
			Msg:     l.svcCtx.Trans.Trans(l.ctx, i18n.Success),
		},
		Data: &types.ProductInfo{
			BaseIDInfo: types.BaseIDInfo{
				Id:         data.Id,
				CreatedAt:  data.CreatedAt,
				UpdatedAt:  data.UpdatedAt,
				DeletedAt:  data.DeletedAt,
				CanAccept:  option.CanAccept,
				CanReject:  option.CanReject,
				CanRemove:  option.CanRemove,
				CanRestore: option.CanRestore,
			},
			Title:          data.Title,
			Slug:           data.Slug,
			ProductCode:    data.ProductCode,
			CategoryId:     data.CategoryId,
			Content:        data.ContentFiltered,
			ContentHtml:    data.Content,
			Tracing:        data.TracingFiltered,
			TracingHtml:    data.Tracing,
			Excerpt:        data.Excerpt,
			SellPoint:      sellPoint,
			StockUnit:      stockUnit,
			ShowStock:      showStock,
			Abbr:           abbr,
			ParentId:       data.ParentId,
			AuthorId:       data.AuthorId,
			Status:         data.Status,
			PendingStatus:  pendingStatus,
			CommentStatus:  data.CommentStatus,
			Sort:           data.Sort,
			PublicTime:     publicTime,
			PublicDate:     publicDate,
			Uri:            uri,
			VirtualType:    data.VirtualType,
			Sticky:         data.Sticky,
			PhotoCount:     photoCount,
			ViewCount:      viewCount,
			ForwardCount:   forwardCount,
			CommentCount:   data.CommentCount,
			GemPriceMin:    gemPriceMin,
			GemPriceMax:    gemPriceMax,
			RetailPriceMin: retailPriceMin,
			RetailPriceMax: retailPriceMax,
			SaleCount:      saleCount,
			IsNew:          isNew,
			IsHot:          isHot,
			Sync:           sync,
			SyncAt:         syncAt,
			Stock:          stock,
			PerfumeId:      relationPerfumeId,
			PerfumeName:    relationPerfumeName,
			PerfumeSlug:    relationPerfumeSlug,
			Perfume:        perfume,
			BrandId:        relationBrandId,
			BrandName:      relationBrandName,
			BrandSlug:      relationBrandSlug,
			Brand:          brand,
			CanSubmit:      option.CanSubmit,
			CanSync:        option.CanSync,
			CanShelf:       option.CanShelf,
			CanOffShelf:    option.CanOffShelf,
			Loaded:         pointy.GetPointer(nowString),
			PreviewLink:    pointy.GetPointer(l.svcCtx.Config.Query.PreviewLink + "&productId=" + strconv.FormatInt(int64(*data.Id), 10)),

			Meta:       meta,
			Media:      media,
			Categories: categories,
			Tags:       tags,
			Attrs:      attrs,
			Specs:      specs,
			Skus:       skus,
		},
	}, nil
}
